/*******************************************************************************
 * Copyright (c) 2012, 2013 XML-Print Project.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * 
 * Contributors:
 *     XML-Print Project - initial API and implementation
 ******************************************************************************/
package info.xmlprint.core.xml;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.apache.log4j.Logger;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

public class PElementGroup {
	private Logger logger = Logger.getLogger(this.getClass().getName());

	private Map<String, PElement> map = new HashMap<String, PElement>();
	private Map<String, String> namespaceMap = new HashMap<String, String>();

	private int noPrefixCount = 0;
	private String mainNamespacePrefix;
	private String mainNamespaceUri;

	public PElementGroup() {
	}

	public void read(InputSource is) {
		SAXParserFactory factory = SAXParserFactory.newInstance();
		factory.setValidating(false);
		SAXParser saxParser = null;
		XmlHandler xh = new XmlHandler();
		try {
			saxParser = factory.newSAXParser();
			saxParser.parse(is, xh);
		} catch (ParserConfigurationException e) {
			Logger.getLogger(this.getClass().getName()).error("", e);
			e.printStackTrace();
		} catch (SAXException e) {
			Logger.getLogger(this.getClass().getName()).error("", e);
			e.printStackTrace();
		} catch (IOException e) {
			Logger.getLogger(this.getClass().getName()).error("", e);
			e.printStackTrace();
		}

	}

	public void setMap(Map<String, PElement> map) {
		this.map = map;
	}

	public void setNamespaceMap(Map<String, String> map) {
		this.namespaceMap = map;
	}

	public PElementGroup clone() {
		PElementGroup xp = new PElementGroup();

		// map1
		Map<String, PElement> cloneMap = new HashMap<String, PElement>();
		Iterator<Entry<String, PElement>> it = map.entrySet().iterator();
		while (it.hasNext()) {
			String peName = it.next().getKey();
			PElement pe = map.get(peName).clone();
			cloneMap.put(peName, pe);
		}
		xp.setMap(cloneMap);

		// map2
		Map<String, String> cloneMap2 = new HashMap<String, String>();
		Iterator<Entry<String, String>> it2 = namespaceMap.entrySet()
				.iterator();
		while (it2.hasNext()) {
			String key = it2.next().getKey();
			String value = namespaceMap.get(key);
			cloneMap2.put(key, value);
		}
		xp.setNamespaceMap(cloneMap2);
		xp.setMainNamespacePrefix(mainNamespacePrefix + "");
		xp.setMainNamespaceUri(mainNamespaceUri + "");
		xp.setNoPrefixCount(noPrefixCount);

		return xp;
	}

	public void setMainNamespacePrefix(String s) {
		mainNamespacePrefix = s;
	}

	public String getMainNamespaceUri() {
		return mainNamespaceUri;
	}

	public void setMainNamespaceUri(String mainNamespaceUri) {
		this.mainNamespaceUri = mainNamespaceUri;
	}

	public String getMainNamespacePrefix() {
		return mainNamespacePrefix;
	}

	public void setNoPrefixCount(int i) {
		noPrefixCount = i;
	}

	public int size() {
		return map.size();
	}

	public int noPrefixCount() {
		return noPrefixCount;
	}

	public PElement getElement(String name) {
		return map.get(name);
	}

	public PElement[] getElements() {

		List<String> sortedList = new ArrayList<String>();
		sortedList.addAll(map.keySet());
		Collections.sort(sortedList);

		PElement[] elements = new PElement[this.size()];

		Iterator<String> it = sortedList.iterator();
		int i = 0;
		while (it.hasNext()) {
			elements[i] = map.get(it.next());
			i++;
		}
		/*
		 * Iterator<Entry<String, PElement>> it = map.entrySet().iterator();
		 * PElement[] elements = new PElement[this.size()]; int i = 0; while
		 * (it.hasNext()) { elements[i] = it.next().getValue(); i++; }
		 */
		return elements;
	}

	public void add(String elemName, String attriName, String value) {
		if (elemName == null || elemName.isEmpty())
			return;

		if (!map.containsKey(elemName)) {
			map.put(elemName, new PElement(elemName));
		}
		map.get(elemName).addAttribut(attriName, value);
		addNameSpace(elemName, attriName, value);
	}

	public void addNameSpace(String elemName, String nsPrefix, String nsValue) {
		if (nsPrefix == null || nsPrefix.isEmpty())
			return;

		String prefix = null;
		// mit nameSpace-prefix
		if (nsPrefix.matches("^xmlns:.*")) {
			prefix = nsPrefix.replaceAll("^xmlns:", "");
		} else if (nsPrefix.equals("xmlns")) {
			// ohne nameSpace-prefix
			prefix = getAutoPrefix(elemName);
			if (mainNamespacePrefix == null) {
				mainNamespacePrefix = prefix;
				mainNamespaceUri = nsValue;

			}
			noPrefixCount++;
		}

		if (prefix == null)
			return;

		namespaceMap.put(prefix, nsValue);
	}

	private String getAutoPrefix(String prefix) {
		if (!namespaceMap.containsKey(prefix))
			return prefix;

		for (int i = 0; namespaceMap.containsKey(prefix); i++) {
			prefix += i;
		}
		return prefix;
	}

	public Map<String, String> getNamespaceMap() {
		return namespaceMap;
	}

	public void namespaceToDisplay() {

		Iterator<Entry<String, String>> it = namespaceMap.entrySet().iterator();
		while (it.hasNext()) {
			String key = it.next().getKey();
			System.out.println(key + "=" + namespaceMap.get(key));
		}
	}

	private class XmlHandler extends DefaultHandler {

		public void startDocument() {

		}

		public void endDocument() {
		}

		public void startElement(String uri, String localName, String qName,
				Attributes atts) {

			String attriName = null, value = null;
			if (atts.getLength() < 1) {
				add(qName, attriName, value);
			} else
				for (int i = 0; i < atts.getLength(); i++) {
					attriName = atts.getQName(i);
					value = atts.getValue(i);
					add(qName, attriName, value);
				}

		}

		public void endElement(String uri, String localName, String qName) {
		}

		public void characters(char[] ch, int start, int length) {
		}

		public org.xml.sax.InputSource resolveEntity(String publicId,
				String systemId) throws org.xml.sax.SAXException,
				java.io.IOException {
			// System.out.println("Ignoring: " + publicId + ", " + systemId);
			logger.warn("Ignoring: " + publicId + ", " + systemId);
			return new org.xml.sax.InputSource(new java.io.StringReader(""));
		};
	}

	public static void main(String args[]) throws IOException {

		String xml = "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
				+ "<TEI xmlns:tei=\"ttt\">" + "<TEI xmlns=\"t22\" />"
				+ "<TEI xmlns=\"t33\" />" + "<text><t><p /></t></text>"
				+ " <h1:text xmlns:h1=\"h1\"><t><p /></t></h1:text>"
				+ "<h2:text xmlns:h2=\"h2\"><t><p /></t></h2:text>"
				+ "<h3 xmlns=\"h3.org\" />" + "</TEI>";

		PElementGroup peg = new PElementGroup();
		peg.read(new InputSource(new ByteArrayInputStream(xml.getBytes())));

		//
		peg = new PElementGroup();
		File file = new File("e://testdokument.xml");
		peg.read(new InputSource(new FileInputStream(file)));
		System.out.println(peg.getMainNamespaceUri());
	}

}
